﻿using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
using System.Linq;
using System.Workflow.ComponentModel.Compiler;
using System.Workflow.ComponentModel.Serialization;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Activities.Rules;
using Microsoft.SharePoint.Workflow;
using System.Xml;
using System.Reflection;
using System.Globalization;
using Microsoft.SharePoint.WorkflowActions;

namespace DP.Sharepoint.SiteWorkflow.Activities
{
    /// <summary>
    /// this activity maps parameter passed from SPWorkflowManager to dependency properties of the workflow 
    /// 
    /// </summary>
    [ActivityToolboxDisplay("VSTTSharePointWorkflow", true), ToolboxItem(typeof(ActivityToolboxItem))]
    public partial class InitiationPropertiesMapperActivity : Activity
    {

        [Description("ActivationProperties")]
        [ValidationOption(ValidationOption.Required)]
        [Browsable(true)]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]

        public SPWorkflowActivationProperties ActivationProperties
        {
            get { return (SPWorkflowActivationProperties)GetValue(ActivationPropertiesProperty); }
            set { SetValue(ActivationPropertiesProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ActivationProperties.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ActivationPropertiesProperty =
            DependencyProperty.Register("ActivationProperties", typeof(SPWorkflowActivationProperties), typeof(InitiationPropertiesMapperActivity));

        public InitiationPropertiesMapperActivity()
        {
            InitializeComponent();
        }

        protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
        {
            if (ActivationProperties == null)
                throw new ArgumentNullException("ActivationProperties");

            var workflow = FindWorkfkow(Parent);

            if (workflow == null)
                throw new ArgumentNullException("Workflow");

            if (string.IsNullOrEmpty(ActivationProperties.InitiationData))
                return base.Execute(executionContext);

            XmlDocument xDoc = new XmlDocument();

            var _namespaceManager = new XmlNamespaceManager(xDoc.NameTable);

            _namespaceManager.AddNamespace("d", "http://schemas.microsoft.com/office/infopath/2009/WSSList/dataFields");
            //load up parameters xml microformat
            xDoc.LoadXml(ActivationProperties.InitiationData);

            foreach (XmlNode node in xDoc.SelectNodes("//d:SharePointListItem_RW/*", _namespaceManager)) //spin thru parameter elements
            {
                string propertyName = node.LocalName.Replace("d:", "");

                string value = node.InnerText;

                TrySetPublicProperty(workflow, propertyName, value);


            }


            return base.Execute(executionContext);
        }


        /// <summary>
        /// attemps to set a public property on workflow object using reflection
        /// </summary>
        /// <param name="workflow"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static bool TrySetPublicProperty(Activity workflow, string propertyName, string value)
        {
            PropertyInfo pi = workflow.GetType().GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);

            if (pi != null)
            {
                try
                {
                    pi.SetValue(workflow, StringToTypedValue(value, pi.PropertyType, System.Threading.Thread.CurrentThread.CurrentUICulture), null);
                }
                catch
                {
                    return false;

                }

            }

            return false;

        }

        public static object StringToTypedValue(string SourceString, Type TargetType, CultureInfo Culture)
        {


            object Result = null;

            if (TargetType == typeof(string))
                Result = SourceString;
            else if (TargetType == typeof(int))
                Result = int.Parse(SourceString,
                           System.Globalization.NumberStyles.Integer, Culture.NumberFormat);
            else if (TargetType == typeof(byte))
                Result = Convert.ToByte(SourceString);
            else if (TargetType == typeof(decimal))
                Result = Decimal.Parse(SourceString, System.Globalization.NumberStyles.Any, Culture.NumberFormat);
            else if (TargetType == typeof(double))
                Result = Double.Parse(SourceString, System.Globalization.NumberStyles.Any, Culture.NumberFormat);
            else if (TargetType == typeof(bool))
            {
                if (SourceString.ToLower() == "true" || SourceString.ToLower() == "on" || SourceString == "1")
                    Result = true;
                else
                    Result = false;
            }
            else if (TargetType == typeof(DateTime))
                Result = Convert.ToDateTime(SourceString, Culture.DateTimeFormat);
            else if (TargetType.IsEnum)
                Result = Enum.Parse(TargetType, SourceString);
            else
            {
                System.ComponentModel.TypeConverter converter =

                          System.ComponentModel.TypeDescriptor.GetConverter(TargetType);
                if (converter != null && converter.CanConvertFrom(typeof(string)))
                    Result = converter.ConvertFromString(SourceString);
                else
                {
                    throw (new Exception("Type Conversion not handled in StringToTypedValue"));
                }
            }


            return Result;
        }

        /// <summary>
        /// locates the Workflow that is hosting the activity by using recursion
        /// </summary>
        /// <param name="activity"></param>
        /// <returns></returns>
        private static Activity FindWorkfkow(Activity activity)
        {
            if (activity.Parent == null)
                return activity;
            else
                return FindWorkfkow(activity.Parent);

        }
    }
}
